import type {
  AssignmentExpression,
  BaseNode,
  CallExpression,
  ExportSpecifier,
  Identifier,
  Literal,
  MemberExpression,
  MethodDefinition,
  Property,
} from 'estree'

export function isProperty(node: BaseNode): node is Property {
  return node.type === 'Property'
}

export function isIdentifier(node: BaseNode): node is Identifier {
  return node.type === 'Identifier'
}

export function isAssignmentExpression(node: BaseNode): node is AssignmentExpression {
  return node.type === 'AssignmentExpression'
}

export function isCallExpression(node: BaseNode): node is CallExpression {
  return node.type === 'CallExpression'
}

export function isMemberExpression(node: BaseNode): node is MemberExpression {
  return node.type === 'MemberExpression'
}

export function isMethodDefinition(node: BaseNode): node is MethodDefinition {
  return node.type === 'MethodDefinition'
}

export function isExportSpecifier(node: BaseNode): node is ExportSpecifier {
  return node.type === 'ExportSpecifier'
}

export function isLiteral(node: BaseNode): node is Literal {
  return node.type === 'Literal'
}

export function isReference(node: BaseNode, parent: BaseNode): boolean {
  if (isMemberExpression(node)) {
    return !node.computed && isReference(node.object, node)
  }
  if (isIdentifier(node)) {
    if (isMemberExpression(parent))
      return parent.computed || node === parent.object
      // `bar` in { bar: foo }
    if (isProperty(parent) && node !== parent.value)
      return false
    // `bar` in `class Foo { bar () {...} }`
    if (isMethodDefinition(parent))
      return false
    // `bar` in `export { foo as bar }`
    if (isExportSpecifier(parent) && node !== parent.local)
      return false
    return true
  }
  return false
}

export function createLiteral(value: string) {
  return {
    type: 'Literal',
    value,
    raw: `'${value}'`,
  } as Literal
}

export function createIdentifier(name: string) {
  return {
    type: 'Identifier',
    name,
  } as Identifier
}

export function createCallExpression(callee: unknown, args: unknown[]) {
  return {
    type: 'CallExpression',
    callee,
    arguments: args,
  } as CallExpression
}
